#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include "wikiproclib.h"

typedef struct {
	int nrpages;
	wikipage* pages;
	int round;
} dumpthreaddata;

void *dump_thread_code(void* tdptr){
	const dumpthreaddata* tdata = ((dumpthreaddata *) tdptr);
	int i;
	FILE* dump;
	char filename[128];

	sprintf(filename, "dump-%d",tdata -> round);

	dump = fopen(filename,"w");
	for(i = 0; i < tdata -> nrpages; i++ ) {
		fprintf(dump, "%d\t%s\t%d\t%d\t%f\t%f\t%f\n",
				i,tdata -> pages[i].title, tdata -> pages[i].id, tdata -> pages[i].namespace, tdata -> pages[i].loc.x, tdata -> pages[i].loc.y, tdata -> pages[i].loc.z);
	}

	fclose(dump);

	printf("Dump thread finished\n");
	return NULL;
}

typedef struct {
	int id;
	wikipage* pages;
	int from;
	int to;
	loc* vectors;
} threaddata;


void *runner_thread_code(void* tdptr) {
	threaddata* tdata = ((threaddata *) tdptr);
	long cntr;


	printf("Thread %d starting... [%d -> %d]\n", tdata -> id, tdata -> from, tdata -> to);
	for(cntr = tdata -> from; cntr < tdata -> to - 1; cntr ++) {
		int nroflinks = tdata -> pages[cntr].nroflinks;
		int linkcntr;
		int icntr;
		for(linkcntr = 0; linkcntr < nroflinks; linkcntr ++) {
			int idx = tdata -> pages[cntr].links[linkcntr];
			tdata -> vectors[cntr - tdata -> from] =
				add(
						tdata -> vectors[cntr -tdata -> from],
						attraction(tdata -> pages[cntr].loc, tdata -> pages[idx].loc));
		}
		for(icntr = tdata -> from; icntr < tdata -> to - 1; icntr ++) {
			tdata -> vectors[icntr - tdata -> from] = add(
					tdata -> vectors[icntr - tdata -> from],
					repulsion(tdata -> pages[cntr].loc, tdata -> pages[icntr].loc));
		}
		if(cntr % 10000 == 0) {
			printf(".");
			fflush(stdout);
		}
	}

	printf("Thread %d finished\n", tdata -> id);
	return NULL;
}

void* apply_thread_code(void* tdptr) {
	const threaddata* tdata = ((threaddata *) tdptr);
	int i;
	for(i = tdata -> from; i < tdata -> to; i++) {
		tdata -> pages[i + tdata -> from].loc = sub(
				tdata -> pages[i + tdata -> from].loc,
				tdata -> vectors[tdata -> from + i]);
		printf("%d\n",i);
	}
	return NULL;
}

int main(int argc, char** argv) {
	int i, r;
	int from = 0;
	pthread_t threads[PROCS];
	threaddata thread_args[PROCS];

	dumpthreaddata dumpthreaddata;
	pthread_t dumpthread;

	wikipage* pages;
	int nr_pages;
	int* index;
	id_idx idindex;

	index = malloc(65536 * sizeof(int));
	for(i = 0; i< 65536; i++) {
		index[i] = -1;
	}

	if(argc != 4) {
		printf("usage:\n");
		printf("wikiproc <pages> <categories> <links>\n");
		exit(EXIT_FAILURE);
	}

	printf("Loading data...\n");

	printf("\t-pages: %s\n",argv[1]);
	pages = load_pages(argv[1], index, &nr_pages);

	printf("\t-generating page id index\n");
	idindex = create_id_index(pages, nr_pages);

	printf("\t-categories: %s\n",argv[2]);
	load_categories(pages, nr_pages, argv[2]);

	printf("\t-links: %s\n",argv[3]);
	load_links(pages, nr_pages, index, &idindex, argv[3]);

	printf("Starting to work...\n");

	//first round, no dump thread, no vectors to apply
	for(i = 0; i < PROCS; i++) {
		thread_args[i].id = i;
		thread_args[i].pages = pages;
		thread_args[i].from = from;
		thread_args[i].vectors = NULL;
		thread_args[i].to = from + (nr_pages / PROCS);
		thread_args[i].vectors = malloc(sizeof(loc) * (thread_args[i].to - thread_args[i].from));
		memset(thread_args[i].vectors, 0, sizeof(loc) * (thread_args[i].to - thread_args[i].from));
		from = from + (nr_pages / PROCS);
		pthread_create(&threads[i], NULL, runner_thread_code, (void*) &thread_args[i]);
	}

	dumpthreaddata.nrpages = nr_pages;
	dumpthreaddata.pages = pages;
	dumpthreaddata.round = 0;
	pthread_create(&dumpthread, NULL, dump_thread_code, (void*) &dumpthreaddata );

	for(i = 0; i < PROCS; i++) {
		printf("Join thread %d\n", i);
		pthread_join(threads[i], NULL);
	}

	//All the other rounds
	for(r = 0; r < 100; r++) {
		printf("--->>> Round %d\n", r);

		for(i = 0; i < PROCS; i++) {
			pthread_create(&threads[i], NULL, apply_thread_code, (void*) &thread_args[i]);
		}
		printf("--->>> Applied changes from prev round %d\n", r);

		for(i = 0; i < PROCS; i++) {
			pthread_join(threads[i], NULL);
		}
		printf("--->>> finished, computing new vectors\n");

		for(i = 0; i < PROCS; i++) {
			pthread_create(&threads[i], NULL, runner_thread_code, (void*) &thread_args[i]);
		}

		dumpthreaddata.round = r;
		pthread_create(&dumpthread, NULL, dump_thread_code, (void*) &dumpthreaddata );
		pthread_join(dumpthread, NULL);

		for(i = 0; i < PROCS; i++) {
			pthread_join(threads[i], NULL);
		}
		printf("--->>> done\n");

	}

	exit(EXIT_SUCCESS);
}

